#include "stdio.h"
#include "camera.h"
#include "sys/select.h"
#include <pthread.h>
#include "venc.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/queue.h>
#include <stdlib.h>

#define VENC_ENABLE 1

#define BUFFER_NUM 4
#define IMAGE_WITH 640
#define IMAGE_HEIGH 640
#define IMAGE_FORMAT V4L2_PIX_FMT_NV12

enum
{
    E_STATE_RUNING,
    E_STATE_EXIT,
} AppState;

static int state;
static Venc_Device_t *enc_dev;

typedef struct Cam_OutQueue_s
{
    SLIST_ENTRY(node) next;
    int buf_id;
} Cam_OutQueue_t;

typedef struct Enc_InQueue_s
{
    SLIST_ENTRY(node) next;
    int buf_id;
} Enc_InQueue_t;

SLIST_HEAD(slisthead, Enc_InQueue_t);

struct slisthead *cam_out_queue = 0;
struct slisthead *enc_in_queue = 0;

void SaveToFile(char *path, void *p, int len)
{
    int fd, ret;

    fd = open(path, O_RDWR | O_CREAT);
    if (fd <= 0)
    {
        printf("open fail\n");
    }
    while(len > 0)
    {
        ret = write(fd, p, len);
        if (ret > 0)
        {
            len -= ret;
            printf("write %d to file\n", ret);
        }
    }
    close(fd);
}

static void *venc_thread(void *argv)
{
    Venc_Device_t *device = (Venc_Device_t *)argv;
    printf("venc_thread start\n");
    while (state == E_STATE_RUNING)
    {
        sleep(1);
    }
    printf("venc_thread end\n");
}

static void *camera_thread(void *argv)
{
    fd_set fds;
    int buf_id, ret;
    char path[64] = {0};
    Cam_Device_t *device = (Cam_Device_t *)argv;
    int index = 0;
    printf("camera_thread start\n");

    Camera_StreamOn(device);

    FD_ZERO(&fds);
    FD_SET(device->fd, &fds);

    while (state == E_STATE_RUNING)
    {
        ret = select(device->fd + 1, &fds, NULL, NULL, NULL);
        if (ret < 0)
        {
            printf("select fail\n");
            break;
        }
        else if (ret == 0)
        {
            printf("select timeout\n");
            continue;
        }
        buf_id = Cam_DequeueBuf(device);

        //send to enc thread
#if VENC_ENABLE
        if (Venc_EnqueueBuf(enc_dev, buf_id) != 0)
        {
            printf("Venc_EnqueueBuf fail\n");
            goto NEXT_FRAME;
        }
        Venc_DequeueBuf(enc_dev);

        
        Venc_EsPacket_t Packet;

        if (Venc_GetEsBuf(enc_dev, &Packet) != 0)
        {
            printf("Venc_GetEsBuf fail\n");
            goto NEXT_FRAME;
        }
        printf("get es buf len %d\n", Packet.len);
        sprintf(path, "./output/enc_out_%d.jpeg", index++);
        SaveToFile(path, Packet.data, Packet.len);
        Venc_PutEsBuf(enc_dev, &Packet);
        
NEXT_FRAME:

#endif

        Cam_EnqueueBuf(device, buf_id);
        //printf("dequeue one frame %d\n", buf_id);
        //sprintf(path, "./output/cam_out_%d.yuv", index++);
        //SaveToFile(path, device->buf[buf_id].start, device->buf[buf_id].length);
    }
    Camera_StreamOff(device);
    printf("camera_thread end\n");

}

int main(int argc, char *argv[])
{
    Cam_Device_t *camdev;
    Venc_Device_t *encdev;
    int i, ret;
    pthread_t camera_task, venc_task;
    pthread_attr_t attr;
    char key = 0;
    Venc_Config_t enc_cfg;
    Cam_Config_t  cam_cfg;

    printf("start run %s\n", argv[0]);
    pthread_attr_init(&attr);

    //init camera device
    cam_cfg.buf_num = BUFFER_NUM;
    cam_cfg.fmt = IMAGE_FORMAT;
    cam_cfg.width = IMAGE_WITH;
    cam_cfg.height = IMAGE_HEIGH;
    camdev = Camera_Init(&cam_cfg);
    if (!camdev)
    {
        return -1;
    }
    cam_out_queue = (struct slisthead*)malloc(sizeof(struct slisthead));
    SLIST_INIT(cam_out_queue);

#if VENC_ENABLE
    //init venc device
    enc_cfg.width = IMAGE_WITH;
    enc_cfg.height = IMAGE_HEIGH;
    enc_cfg.fmt = MPP_FMT_YUV420SP;
    enc_cfg.rc_mode = 1;
    enc_cfg.buf_num = BUFFER_NUM;
    encdev = Venc_Init(&enc_cfg);
    if (!encdev)
    {
        Camera_DeInit(camdev);
        return -1;
    }

    //transfor dmabuf to mpp buf
    for (i = 0; i < BUFFER_NUM; i++)
    {
        Venc_TransformDmaBufToMppBuf(camdev->buf[i].fd, &encdev->frame_buf[i], camdev->buf[i].length);
    }
    enc_dev = encdev;
    pthread_create(&venc_task, &attr, (void *)venc_thread, encdev);
#endif

    //enqueue buf
    for (i = 0; i < BUFFER_NUM; i++)
    {
        Cam_EnqueueBuf(camdev, i);
    }
    pthread_create(&camera_task, &attr, (void *)camera_thread, camdev);
    pthread_attr_destroy(&attr);

    state = E_STATE_RUNING;

    while(key != 'q')
    {
        key = getchar();
    }

    state = E_STATE_EXIT;
    pthread_join(venc_task, NULL);
    pthread_join(camera_task, NULL);

    Venc_Deinit(encdev);

    Camera_DeInit(camdev);

    printf("Exit\n");
}
